package com.hz.sys.config;

import com.alibaba.fastjson.JSON;
import com.hz.sys.utils.WebUtil;
import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * 创建人: mcz
 * 创建日期: 2018/10/30 16:06
 * 描述:  aop请求入参、出参日志打印
 * 版本: 1.0
 */
@Aspect
@Configuration
public class LogRecord_AOP {
    private static final Logger logger = LoggerFactory.getLogger(LogRecord_AOP.class);

    private static String[] types = { "java.lang.Integer", "java.lang.Double",
            "java.lang.Float", "java.lang.Long", "java.lang.Short",
            "java.lang.Byte", "java.lang.Boolean", "java.lang.Char",
            "java.lang.String", "int", "double", "long", "short", "byte",
            "boolean", "char", "float" };

    // 定义切点Pointcut
    @Pointcut("execution(* com.hz.ws..*.*(..))")
    public void excudeService() {
    }


    @AfterReturning(value = "excudeService()", returning = "rtv")
    public void searchControllerCallCalls(JoinPoint joinPoint, Object rtv)  {
        try {
                HttpServletRequest request = WebUtil.getRequest();
                String url = request.getRequestURL().toString();
                String method = request.getMethod();
                String classType = joinPoint.getTarget().getClass().getName();
                Class<?> clazz = Class.forName(classType);
                String clazzName = clazz.getName();
                String methodName = joinPoint.getSignature().getName();
                String[] paramNames = getFieldsName(this.getClass(), clazzName, methodName);
                String logContent = writeLogInfo(paramNames, joinPoint);
                //Logger logger = LoggerFactory.getLogger(clazzName);
                logger.info("请求路径:{},请求方式{}",url,method);
                logger.info("目标方法全限定名:{}.{},请求参数:{}",clazzName,methodName, logContent);
                logger.info("获取返回值:{}",JSON.toJSON(rtv));
        } catch (Exception e) {
            logger.error("AOP参数拦截异常{}",e.toString());
        }

    }
    private static String writeLogInfo(String[] paramNames, JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        StringBuilder sb = new StringBuilder();
        boolean clazzFlag = true;
        if(args!=null){
            for(int k=0; k<args.length; k++){
                Object arg = args[k];
                    sb.append(paramNames[k]).append(" ");
                    if(arg!=null){
                        // 获取对象类型
                        String typeName = arg.getClass().getTypeName();
                        for (String t : types) {
                            if (t.equals(typeName)) {
                                sb.append("=").append(arg).append("; ");
                            }
                        }
                    }else{
                        sb.append("=").append("null").append("; ");
                    }
                sb.append(getFieldsValue(arg));
            }
        }
        return sb.toString();
    }

    /**
     * 得到方法参数的名称
     * @param cls
     * @param clazzName
     * @param methodName
     * @return
     * @throws NotFoundException
     */
    private static String[] getFieldsName(Class cls, String clazzName, String methodName) throws NotFoundException {
        ClassPool pool = ClassPool.getDefault();
        ClassClassPath classPath = new ClassClassPath(cls);
        pool.insertClassPath(classPath);
        CtClass cc = pool.get(clazzName);
        CtMethod cm = cc.getDeclaredMethod(methodName);
        MethodInfo methodInfo = cm.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        if (attr == null) {
            // exception
        }
        String[] paramNames = new String[cm.getParameterTypes().length];
        int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
        for (int i = 0; i < paramNames.length; i++){
            assert attr != null;
            //paramNames即参数名
            paramNames[i] = attr.variableName(i + pos);
        }
        return paramNames;
    }

    /**
     * 得到参数的值
     * @param obj
     */
    public static String getFieldsValue(Object obj) {
        StringBuilder sb = new StringBuilder();
        if(obj!=null){
            Field[] fields = obj.getClass().getDeclaredFields();
            String typeName = obj.getClass().getTypeName();
            for (String t : types) {
                if(t.equals(typeName))
                    return "";
            }
            sb.append("【");
            for (Field f : fields) {
                f.setAccessible(true);
                try {
                    for (String str : types) {
                        if (f.getType().getName().equals(str)){
                            sb.append(f.getName()).append(" = ").append(f.get(obj)).append("; ");
                        }
                    }
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            sb.append("】");
        }
        return sb.toString();
    }

    private void getParamterName(String clazzName, String methodName) throws NotFoundException {
        ClassPool pool = ClassPool.getDefault();
        ClassClassPath classPath = new ClassClassPath(this.getClass());
        pool.insertClassPath(classPath);
        CtClass cc = pool.get(clazzName);
        CtMethod cm = cc.getDeclaredMethod(methodName);
        MethodInfo methodInfo = cm.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute
                .getAttribute(LocalVariableAttribute.tag);
        if (attr == null) {
            // exception
        }
        String[] paramNames = new String[cm.getParameterTypes().length];
        int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
        for (int i = 0; i < paramNames.length; i++) {
            assert attr != null;
            paramNames[i] = attr.variableName(i + pos);
        }
        // paramNames即参数名
        for (String paramName : paramNames) {
            System.out.println(paramName);
        }
    }

    /**
     * 得到用户的登陆信息--这个还未实现，只是在网上抄的一段
     * @param joinPoint
     * @throws Exception
     */
    public void adminOptionContent(JoinPoint joinPoint) throws Exception {
        StringBuffer rs = new StringBuffer();
        String className;
        int index = 1;
        Object[] args = joinPoint.getArgs();
        for (Object info : args) {
            // 获取对象类型
            className = info.getClass().getName();
            className = className.substring(className.lastIndexOf(".") + 1);
            rs.append("[参数").append(index).append("，类型：").append(className).append("，值：");
            // 获取对象的所有方法
            Method[] methods = info.getClass().getDeclaredMethods();
            // 遍历方法，判断get方法
            for (Method method : methods) {
                String methodName = method.getName();
                System.out.println(methodName);
                // 判断是不是get方法
                if (!methodName.contains("get")) {// 不是get方法
                    continue;// 不处理
                }
                Object rsValue = null;
                try {
                    // 调用get方法，获取返回值
                    rsValue = method.invoke(info);
                    if (rsValue == null) {// 没有返回值
                        continue;
                    }
                } catch (Exception e) {
                    continue;
                }
                // 将值加入内容中
                rs.append("(").append(methodName).append(" : ").append(rsValue).append(")");
            }
            rs.append("]");
            index++;
        }
        System.out.println(rs.toString());
    }


     /* @Around("excudeService()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        HttpServletRequest request = WebUtil.getRequest();
        String url = request.getRequestURL().toString();
        String method = request.getMethod();
        String queryString = request.getQueryString();
        Object[] args = pjp.getArgs();
        String params = "";
        //获取请求参数集合并进行遍历拼接
        if(args!=null&&args.length>0){
            if("POST".equals(method)){
                Object object = args[0];
                Map map = getKeyAndValue(object);
                params = JSON.toJSONString(map);;
            }else if("GET".equals(method)){
                params = queryString;
            }
        }
        System.out.println(pjp.getSignature());
        logger.info("请求目标{}->{}",pjp.getTarget().getClass(), pjp.getSignature().getName());
        logger.info("请求开始地址:{},请求方式:{},请求参数:{}",url,method,params);

        // result的值就是被拦截方法的返回值
        Object result = pjp.proceed();
        logger.info("请求结束获取返回值:{}" + JSON.toJSONString(result));
        return result;
    }

    public static Map<String, Object> getKeyAndValue(Object obj) {
        Map<String, Object> map = new HashMap<>(8);
        // 得到类对象
        Class userCla = (Class) obj.getClass();
        *//* 得到类中的所有属性集合 *//*
        Field[] fs = userCla.getDeclaredFields();
        for (int i = 0; i < fs.length; i++) {
            Field f = fs[i];
            f.setAccessible(true); // 设置些属性是可以访问的
            Object val = new Object();
            try {
                val = f.get(obj);
                // 得到此属性的值
                map.put(f.getName(), val);// 设置键值
            } catch (IllegalArgumentException | IllegalAccessException e) {
                e.printStackTrace();
            }

        }
        return map;
    }*/

}

